<?php

namespace App\Http\Traits;

use App\Models\OperationLog;
use Illuminate\Http\Request;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Log;

trait LogOperation
{
    /**
     * 记录操作日志
     *
     * @param string $action 操作类型：create, update, delete, export, import, view等
     * @param string $resource 资源类型：users, roles, menus等
     * @param mixed $resourceId 资源ID
     * @param string $description 操作描述
     * @param array|null $beforeData 修改前的数据
     * @param array|null $afterData 修改后的数据
     * @param Request|null $request 请求对象，如果为空则使用当前请求
     * @return OperationLog|null
     */
    protected function logOperation(
        string $action,
        string $resource,
        $resourceId = null,
        string $description = '',
        ?array $beforeData = null,
        ?array $afterData = null,
        ?Request $request = null
    ): ?OperationLog {
        try {
            $request = $request ?? request();
            $user = auth()->user();

            if (!$user) {
                return null;
            }

            // 计算变更字段
            $changes = null;
            if ($beforeData !== null && $afterData !== null) {
                $changes = $this->calculateChanges($beforeData, $afterData);
            }

            // 过滤敏感数据
            $filteredBeforeData = $beforeData ? $this->filterSensitiveData($beforeData) : null;
            $filteredAfterData = $afterData ? $this->filterSensitiveData($afterData) : null;

            return OperationLog::create([
                'user_id' => $user->id,
                'username' => $user->username,
                'method' => $request->method(),
                'action' => $action,
                'resource' => $resource,
                'resource_id' => $resourceId,
                'description' => $description ?: $this->generateDescription($action, $resource, $resourceId),
                'path' => $request->path(),
                'params' => $this->filterParams($request->all()),
                'before_data' => $filteredBeforeData,
                'after_data' => $filteredAfterData,
                'changes' => $changes,
                'ip' => $request->ip(),
                'user_agent' => $request->userAgent(),
                'status' => 'success',
            ]);
        } catch (\Exception $e) {
            // 记录日志失败不应该影响请求
            Log::error('操作日志记录失败: ' . $e->getMessage(), [
                'action' => $action,
                'resource' => $resource,
                'resource_id' => $resourceId,
                'error' => $e->getMessage(),
            ]);
            return null;
        }
    }

    /**
     * 记录创建操作
     *
     * @param string $resource 资源类型
     * @param Model $model 创建的模型
     * @param string $description 操作描述
     * @param Request|null $request
     * @return OperationLog|null
     */
    protected function logCreate(
        string $resource,
        Model $model,
        string $description = '',
        ?Request $request = null
    ): ?OperationLog {
        $data = $this->modelToArray($model);
        return $this->logOperation(
            'create',
            $resource,
            $model->id,
            $description ?: "创建{$resource}: {$this->getModelIdentifier($model)}",
            null,
            $data,
            $request
        );
    }

    /**
     * 记录更新操作
     *
     * @param string $resource 资源类型
     * @param Model $model 更新的模型
     * @param array $originalData 更新前的数据
     * @param string $description 操作描述
     * @param Request|null $request
     * @return OperationLog|null
     */
    protected function logUpdate(
        string $resource,
        Model $model,
        array $originalData = [],
        string $description = '',
        ?Request $request = null
    ): ?OperationLog {
        // 使用提供的原始数据，如果没有提供则使用空数组
        $beforeData = $originalData;
        
        // 获取更新后的数据（模型已经更新，直接转换为数组）
        $afterData = $this->modelToArray($model);
        
        return $this->logOperation(
            'update',
            $resource,
            $model->id,
            $description ?: "更新{$resource}: {$this->getModelIdentifier($model)}",
            $beforeData,
            $afterData,
            $request
        );
    }

    /**
     * 记录删除操作
     *
     * @param string $resource 资源类型
     * @param Model $model 删除的模型
     * @param string $description 操作描述
     * @param Request|null $request
     * @return OperationLog|null
     */
    protected function logDelete(
        string $resource,
        Model $model,
        string $description = '',
        ?Request $request = null
    ): ?OperationLog {
        $data = $this->modelToArray($model);
        return $this->logOperation(
            'delete',
            $resource,
            $model->id,
            $description ?: "删除{$resource}: {$this->getModelIdentifier($model)}",
            $data,
            null,
            $request
        );
    }

    /**
     * 记录导出操作
     *
     * @param string $resource 资源类型
     * @param int $count 导出数量
     * @param string $description 操作描述
     * @param Request|null $request
     * @return OperationLog|null
     */
    protected function logExport(
        string $resource,
        int $count = 0,
        string $description = '',
        ?Request $request = null
    ): ?OperationLog {
        return $this->logOperation(
            'export',
            $resource,
            null,
            $description ?: "导出{$resource}数据（{$count}条）",
            null,
            ['count' => $count],
            $request
        );
    }

    /**
     * 记录导入操作
     *
     * @param string $resource 资源类型
     * @param int $successCount 成功数量
     * @param int $failCount 失败数量
     * @param string $description 操作描述
     * @param Request|null $request
     * @return OperationLog|null
     */
    protected function logImport(
        string $resource,
        int $successCount = 0,
        int $failCount = 0,
        string $description = '',
        ?Request $request = null
    ): ?OperationLog {
        return $this->logOperation(
            'import',
            $resource,
            null,
            $description ?: "导入{$resource}数据（成功:{$successCount}条，失败:{$failCount}条）",
            null,
            [
                'success_count' => $successCount,
                'fail_count' => $failCount,
            ],
            $request
        );
    }

    /**
     * 记录批量操作
     *
     * @param string $action 操作类型
     * @param string $resource 资源类型
     * @param array $resourceIds 资源ID数组
     * @param string $description 操作描述
     * @param Request|null $request
     * @return OperationLog|null
     */
    protected function logBatch(
        string $action,
        string $resource,
        array $resourceIds,
        string $description = '',
        ?Request $request = null
    ): ?OperationLog {
        return $this->logOperation(
            $action,
            $resource,
            null,
            $description ?: "批量{$action}{$resource}（" . count($resourceIds) . "条）",
            null,
            ['ids' => $resourceIds, 'count' => count($resourceIds)],
            $request
        );
    }

    /**
     * 计算变更字段
     *
     * @param array $beforeData
     * @param array $afterData
     * @return array
     */
    protected function calculateChanges(array $beforeData, array $afterData): array
    {
        $changes = [];

        // 检查所有字段的变更
        $allKeys = array_unique(array_merge(array_keys($beforeData), array_keys($afterData)));

        foreach ($allKeys as $key) {
            $beforeValue = $beforeData[$key] ?? null;
            $afterValue = $afterData[$key] ?? null;

            // 如果是数组，递归比较
            if (is_array($beforeValue) && is_array($afterValue)) {
                $nestedChanges = $this->calculateChanges($beforeValue, $afterValue);
                if (!empty($nestedChanges)) {
                    $changes[$key] = $nestedChanges;
                }
            } elseif ($beforeValue !== $afterValue) {
                $changes[$key] = [
                    'before' => $beforeValue,
                    'after' => $afterValue,
                ];
            }
        }

        return $changes;
    }

    /**
     * 过滤敏感数据
     *
     * @param array $data
     * @return array
     */
    protected function filterSensitiveData(array $data): array
    {
        $sensitiveKeys = [
            'password',
            'password_confirmation',
            'old_password',
            'token',
            'api_token',
            'secret',
            'api_key',
            'access_token',
            'refresh_token',
            'private_key',
            'public_key',
        ];

        $maskingService = app(\App\Services\DataMaskingService::class);

        foreach ($data as $key => $value) {
            // 过滤敏感键
            if (in_array(strtolower($key), array_map('strtolower', $sensitiveKeys))) {
                $data[$key] = '***';
                continue;
            }

            // 脱敏手机号
            if (stripos($key, 'phone') !== false && is_string($value)) {
                $data[$key] = $maskingService->maskPhone($value);
                continue;
            }

            // 脱敏邮箱
            if (stripos($key, 'email') !== false && is_string($value)) {
                $data[$key] = $maskingService->maskEmail($value);
                continue;
            }

            // 脱敏身份证
            if (stripos($key, 'id_card') !== false && is_string($value)) {
                $data[$key] = $maskingService->maskIdCard($value);
                continue;
            }

            // 处理嵌套数组
            if (is_array($value)) {
                $data[$key] = $this->filterSensitiveData($value);
            }
        }

        return $data;
    }

    /**
     * 过滤请求参数中的敏感信息
     *
     * @param array $params
     * @return array
     */
    protected function filterParams(array $params): array
    {
        return $this->filterSensitiveData($params);
    }

    /**
     * 将模型转换为数组
     *
     * @param Model $model
     * @return array
     */
    protected function modelToArray(Model $model): array
    {
        $data = $model->toArray();
        
        // 移除时间戳字段（可选，根据需要决定）
        // unset($data['created_at'], $data['updated_at'], $data['deleted_at']);
        
        return $data;
    }

    /**
     * 获取模型标识符（用于描述）
     *
     * @param Model $model
     * @return string
     */
    protected function getModelIdentifier(Model $model): string
    {
        // 优先使用name字段
        if (isset($model->name)) {
            return $model->name;
        }
        // 其次使用username字段
        if (isset($model->username)) {
            return $model->username;
        }
        // 最后使用ID
        return "ID:{$model->id}";
    }

    /**
     * 生成操作描述
     *
     * @param string $action
     * @param string $resource
     * @param mixed $resourceId
     * @return string
     */
    protected function generateDescription(string $action, string $resource, $resourceId = null): string
    {
        $actionMap = [
            'create' => '创建',
            'update' => '更新',
            'delete' => '删除',
            'export' => '导出',
            'import' => '导入',
            'view' => '查看',
        ];

        $actionName = $actionMap[$action] ?? $action;
        $resourceName = $this->getResourceName($resource);
        $idStr = $resourceId ? " (ID:{$resourceId})" : '';

        return "{$actionName}{$resourceName}{$idStr}";
    }

    /**
     * 获取资源名称
     *
     * @param string $resource
     * @return string
     */
    protected function getResourceName(string $resource): string
    {
        $resourceMap = [
            'users' => '用户',
            'roles' => '角色',
            'menus' => '菜单',
            'permissions' => '权限',
            'departments' => '部门',
            'operation_logs' => '操作日志',
            'login_logs' => '登录日志',
            'storage_configs' => '存储配置',
        ];

        return $resourceMap[$resource] ?? $resource;
    }
}

